#!/usr/bin/env node
import process from 'node:process';
import { load as loadHtml } from 'cheerio';

const endpoint = process.env.MCP_BASE_URL || 'http://127.0.0.1:12306/mcp';

const CLIENT_INFO = {
  name: 'mcp-cli-test',
  version: '0.1.0'
};

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const WEB_CONTENT_ARGS = {
  url: 'https://search.jd.com/Search?keyword=%E8%8B%B9%E6%9E%9C%20iPhone%2017%20Pro%20256GB%20%E9%93%B6%E8%89%B2&pvid=2bfa984742ae4960be79f44c69272306',
  htmlContent: true,
  textContent: true
};

const truncateForLog = (value, max = 2000) => {
  if (typeof value !== 'string') {
    try {
      value = JSON.stringify(value);
    } catch (_) {
      value = String(value);
    }
  }
  return value.length > max ? `${value.slice(0, max)}…` : value;
};

const trimText = (value) => (typeof value === 'string' ? value.trim() : '');

const buildSelectorsFromNode = (cardNode, sku) => {
  const bySku = sku ? `[data-sku="${sku}"]` : null;
  const pointId = cardNode.attr('data-point-id') || null;
  const byPointId = pointId ? `[data-point-id="${pointId}"]` : null;

  const titleContainer = cardNode.find('[class*="_goods_title_container"]').first();
  let byTitleContainer = null;
  if (titleContainer.length) {
    const classes = (titleContainer.attr('class') || '')
      .split(/\s+/)
      .filter((cls) => cls.includes('_goods_title_container'));
    if (classes.length) {
      byTitleContainer = `.${classes[0]}`;
    }
  }

  return {
    byTitleContainer,
    bySku,
    byPointId,
    generic: '[class*="_goods_title_container"]'
  };
};

const parseProductCards = (htmlContent) => {
  console.log("[MCPCALL][parseProductCards]", htmlContent);
  if (!htmlContent || typeof htmlContent !== 'string') {
    return [];
  }

  const $ = loadHtml(htmlContent);
  const productCards = $('.plugin_goodsCardWrapper');
  const products = [];

  productCards.each((index, element) => {
    const cardNode = $(element);

    const titleElement = cardNode.find('[class*="_text"]').first();
    const title = trimText(titleElement.text());

    const priceElement = cardNode.find('[class*="_price"]').first();
    let price = '';
    if (priceElement.length) {
      const yenSymbol = trimText(priceElement.find('[class*="_yen"]').first().text());
      const mainPrice = priceElement
        .contents()
        .toArray()
        .filter((node) => node.type === 'text')
        .map((node) => trimText(node.data))
        .join('');
      const decimal = trimText(priceElement.find('[class*="_decimal"]').first().text());
      price = `${yenSymbol}${mainPrice}${decimal}`.trim();
    }

    const sku = cardNode.attr('data-sku') || '';

    let imgUrl = '';
    const imgElement = cardNode.find('[class*="_img"]').first();
    if (imgElement.length) {
      imgUrl = imgElement.attr('src') || imgElement.attr('data-src') || '';
      if (typeof imgUrl === 'string' && imgUrl.startsWith('//')) {
        imgUrl = `https:${imgUrl}`;
      }
    }

    const shopElement = cardNode.find('[class*="_name"] span').first();
    const shopName = trimText(shopElement.text());

    const tags = [];
    cardNode.find('[class*="_textTag"] span, [class*="_imgTag"] img').each((_, node) => {
      const el = $(node);
      const text = trimText(el.text() || el.attr('alt'));
      if (text) {
        tags.push(text);
      }
    });

    const volumeElement = cardNode.find('[class*="_goods_volume"]').first();
    const salesInfo = trimText(volumeElement.text());

    const selectors = buildSelectorsFromNode(cardNode, sku);
    const preferredSelector = selectors.byTitleContainer || selectors.bySku || selectors.byPointId || selectors.generic;

    products.push({
      index: index + 1,
      title,
      price,
      sku,
      selector: preferredSelector || '',
      selectors,
      imgUrl,
      shopName,
      tags,
      salesInfo
    });
  });

  console.log("[MCPCALL][parseProductCards]", products);
  return products;
};

const payloads = [
  {
    label: '获取 JD 搜索页面内容',
    request: {
      jsonrpc: '2.0',
      id: 'call-1',
      method: 'tools/call',
      params: {
        name: 'chrome_get_web_content',
        arguments: WEB_CONTENT_ARGS,
        _meta: {
          progressToken: 1
        }
      }
    }
  }
];

async function sendJsonRpc(body, sessionId) {
  const payloadText = JSON.stringify(body);
  console.log('[MCP TEST] => 请求', {
    method: body?.method,
    name: body?.params?.name,
    hasSession: Boolean(sessionId),
    bodyPreview: truncateForLog(payloadText, 400)
  });

  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      accept: 'application/json, text/event-stream',
      ...(sessionId ? { 'mcp-session-id': sessionId } : {})
    },
    body: payloadText
  });

  const rawText = await response.text();
  const sessionHeader = response.headers.get('mcp-session-id') || response.headers.get('Mcp-Session-Id');
  const contentType = response.headers.get('content-type') || '';

  console.log('[MCP TEST] <= 响应头', {
    status: response.status,
    statusText: response.statusText,
    contentType,
    sessionHeader
  });

  if (!response.ok) {
    console.log('[MCP TEST] <= 响应体(失败)', truncateForLog(rawText, 800));
    throw new Error(`HTTP ${response.status} ${response.statusText}: ${rawText}`);
  }

  let parsed;
  if (contentType.includes('text/event-stream')) {
    parsed = parseEventStream(rawText);
  } else {
    try {
      parsed = JSON.parse(rawText);
    } catch (error) {
      console.log('[MCP TEST] JSON 解析失败，原始内容', truncateForLog(rawText, 800));
      throw new Error(`无法解析 JSON 响应: ${rawText}`);
    }
  }

  console.log('[MCP TEST] <= 解析结果预览', truncateForLog(parsed, 800));
  return { result: parsed, sessionId: sessionHeader || sessionId };
}

function parseEventStream(raw) {
  const events = raw
    .split(/\n\n+/)
    .map((chunk) => chunk.trim())
    .filter(Boolean);

  console.log('[MCP TEST] SSE 事件数量', events.length);

  for (const event of events) {
    console.log('[MCP TEST] SSE 事件原文', truncateForLog(event, 400));
    const dataLines = [];
    for (const line of event.split('\n')) {
      if (line.startsWith('data:')) {
        dataLines.push(line.slice(5).trim());
      }
    }
    if (dataLines.length) {
      const combined = dataLines.join('\n');
      try {
        return JSON.parse(combined);
      } catch (error) {
        console.log('[MCP TEST] SSE 数据解析失败', truncateForLog(combined, 400));
        throw new Error(`无法解析事件流数据为 JSON: ${combined}`);
      }
    }
  }

  throw new Error(`事件流中未找到 data 字段: ${raw}`);
}

const extractHtmlFromResult = (rawResult) => {
  const payload = rawResult?.result ?? rawResult;
  const contentArray = Array.isArray(payload?.content) ? payload.content : [];
  const primaryText = typeof contentArray[0]?.text === 'string' ? contentArray[0].text : null;
  if (!primaryText) {
    return null;
  }

  try {
    const firstParse = JSON.parse(primaryText);
    if (!firstParse?.data?.content) {
      return null;
    }
    const inner = firstParse.data.content[0];
    const innerText = typeof inner?.text === 'string' ? inner.text : null;
    if (!innerText) {
      return null;
    }
    const secondParse = JSON.parse(innerText);
    if (secondParse && typeof secondParse === 'object') {
      if (typeof secondParse.htmlContent === 'string') {
        return secondParse.htmlContent;
      }
      if (typeof secondParse.text === 'string') {
        return secondParse.text;
      }
    }
    return innerText;
  } catch (error) {
    console.warn('[MCP TEST] HTML 提取失败:', error.message);
    return null;
  }
};

async function main() {
  try {
    console.log('=== 初始化 MCP 会话 ===');
    const initBody = {
      jsonrpc: '2.0',
      id: 'init-1',
      method: 'initialize',
      params: {
        clientInfo: CLIENT_INFO,
        capabilities: {},
        protocolVersion: '2025-03-26'
      }
    };

    console.log('[MCP TEST] 请求 Payload:');
    console.log(JSON.stringify(initBody, null, 2));

    const initResponse = await sendJsonRpc(initBody);
    console.log('[MCP TEST] 响应结果:');
    console.log(JSON.stringify(initResponse.result, null, 2));

    let sessionId = initResponse.sessionId;
    if (!sessionId && initResponse.result?.result?.sessionId) {
      sessionId = initResponse.result.result.sessionId;
    }

    if (!sessionId) {
      console.warn('[MCP TEST] 未从初始化响应中获得 sessionId，后续请求可能失败。');
    } else {
      console.log(`[MCP TEST] 会话 ID: ${sessionId}`);
    }

    for (const { label, request } of payloads) {
      console.log(`\n=== 执行: ${label} ===`);
      console.log('[MCP TEST] 请求 Payload:');
      console.log(JSON.stringify(request, null, 2));

      const { result, sessionId: updatedSession } = await sendJsonRpc(request, sessionId);
      sessionId = updatedSession;
      console.log('[MCP TEST] 响应结果:');
      console.log(JSON.stringify(result, null, 2));

      if (request?.params?.name === 'chrome_get_web_content') {
        await delay(1500);
        console.log('[MCP TEST] 工具响应解析完毕，开始处理业务逻辑');
        const htmlContent = extractHtmlFromResult(result);
        if (!htmlContent) {
          console.warn('[MCP TEST] 未提取到 HTML 内容');
          continue;
        }

        const products = parseProductCards(htmlContent);
        console.log(`[MCP TEST] 从页面解析到 ${products.length} 个商品`);
        console.log(JSON.stringify(products.slice(0, 3), null, 2));
      }

      console.log('[MCP TEST] === 本次工具调用处理完成 ===');
    }
  } catch (error) {
    console.error('[MCP TEST] 调用失败:', error);
    process.exitCode = 1;
  }
}

main();
